/* 
 *	Copyright (C) 2009-2010 miaohua
 *	http://code.google.com/p/lrcshower/
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */

#include "StdAfx.h"
#include <Streams.h>
#include <initguid.h>
#include "Resource.h"
#include "WinLrcShowDlg.h"
#include "DeskLrcShowDlg.h"
#include "LrcShowFilter.h"
#include "LrcShowFilterUUID.h"
#include "MusicBoxSetting.h"
#include "LrcShowFilterWrapper.h"

CLrcShowFilterWrapper::CLrcShowFilterWrapper()
{
     //CreateLrcFilter();
}

CLrcShowFilterWrapper::~CLrcShowFilterWrapper()
{
     Release();
}

IPin* CLrcShowFilterWrapper::CheckAudioRender(IBaseFilter* CurFilter)
{
     IEnumPins * EnumPins = NULL;
     PIN_DIRECTION PinDirection;
     // Enumerate all pins on the filter
     if( CurFilter->EnumPins(&EnumPins)  != S_OK )
         return NULL;

     bool HasOutput = false;
     IPin * Pin = NULL;
     IPin * AudioPin =NULL;
     // Step through every pin, looking for wether the filter has output pins
     // As an audio render, the filter mustn't have output pins
     while( EnumPins->Next(1, &Pin, NULL) == S_OK )
     {
            // Get pin's direction
            HRESULT HR = Pin->QueryDirection(&PinDirection);
            if( HR == S_OK )
            {
                if( PinDirection == PINDIR_OUTPUT )
                    HasOutput = true;
                else if( PinDirection == PINDIR_INPUT ) {
                    // Check current pin's media type, it must be audio
                    AM_MEDIA_TYPE MediaType;
                    if( Pin->ConnectionMediaType(&MediaType) == S_OK )
                    {
                        if( MediaType.majortype == MEDIATYPE_Audio )
                        {
                            if( AudioPin ) AudioPin->Release();
                            AudioPin = Pin;
                        }
                        FreeMediaType(MediaType);
                   }
              }
            }  
            // If current pin is an output pin or
            // its' media type is not audio
            if( HasOutput || !AudioPin )
                Pin->Release();
            // If fail to query pin's direction or filter has output pin
            // just break (we need a filter with no output pin)
            if( FAILED(HR) || HasOutput )
                break;
     }
     // Release enumerate
     EnumPins->Release();

     return AudioPin;
}

HRESULT CLrcShowFilterWrapper::AddLrcShowFilterHelper(IPin * AudioPin, IGraphBuilder* FilterGraph)
{
     CComPtr<IPin> UpStreamPin;
     // Get upstream filter's output pin connecting with audio pin
     if( AudioPin->ConnectedTo(&UpStreamPin) == S_OK ) 
     {
         PIN_INFO Info;   
         HRESULT HR = UpStreamPin->QueryPinInfo(&Info);
         if( HR == S_OK )
         {
             // Check wether lrc show filter has been connected into graph
             // sometimes, because filter has been added into graph(in previous song playing), previous render call may connect the filter into graph
             // therefore we don't need to add it 
             if( Info.pFilter == m_LrcShowFilter ) return HR;
             // Remove filter from graph if needed
             FilterGraph->RemoveFilter(m_LrcShowFilter);
             if( (HR=FilterGraph->AddFilter(m_LrcShowFilter, _T("Lrc Show Filter"))) != S_OK )
                 return E_FAIL;
              
             // Get lrc show filter's input & output pins
             CComPtr<IPin> OutputPin = GetOnePin(m_LrcShowFilter, PINDIR_OUTPUT);
             CComPtr<IPin> InputPin  = GetOnePin(m_LrcShowFilter, PINDIR_INPUT);
             if( !OutputPin || !InputPin )
             {
                 FilterGraph->RemoveFilter(m_LrcShowFilter);
                 return E_FAIL;
             }

             // Disconnect audio pin and upstream pin 
             if( FAILED(HR=FilterGraph->Disconnect(AudioPin)) )
             {
                 FilterGraph->RemoveFilter(m_LrcShowFilter);
                 return E_FAIL;
             }
             if( FAILED(HR=FilterGraph->Disconnect(UpStreamPin)) )
             {
                 FilterGraph->RemoveFilter(m_LrcShowFilter);
                 // Reconnect to the original audio pin again
                 FilterGraph->Connect(UpStreamPin, AudioPin);
                 return E_FAIL;
             }
                          
             // Add our lrc show filter
             if( FilterGraph->Connect(UpStreamPin, InputPin) != S_OK )
             {
                 FilterGraph->RemoveFilter(m_LrcShowFilter);
                 // Reconnect to the original audio pin again
                 FilterGraph->Connect(UpStreamPin, AudioPin);
                 return E_FAIL;
             }
             if( FilterGraph->Connect(OutputPin, AudioPin) != S_OK )
             {
                 FilterGraph->Disconnect(UpStreamPin);
                 FilterGraph->Disconnect(InputPin);
                 FilterGraph->RemoveFilter(m_LrcShowFilter);
                 FilterGraph->Connect(UpStreamPin, AudioPin);
                 return E_FAIL;
             }
         }
         return HR;
     }

     return E_FAIL;
}

HRESULT CLrcShowFilterWrapper::AddLrcShowFilter(IGraphBuilder* FilterGraph)
{
     ASSERT(FilterGraph != NULL);

     IEnumFilters* EnumFilters = NULL;
     HRESULT HR = FilterGraph->EnumFilters(&EnumFilters);
     if( FAILED(HR) ) return HR;

     // In FilterGraph, check with filter is audio render
     IBaseFilter* CurFilter = NULL;
     bool AddLrcFilterFlag = false;
     while( EnumFilters->Next(1, &CurFilter, NULL) == S_OK && !AddLrcFilterFlag)
     { 
            // Check wether current filter is a audio render
            IPin* AudioPin = CheckAudioRender(CurFilter);
            CurFilter->Release();
            // If we get an audio pin, then add our lrc filter
            if( AudioPin && (HR = AddLrcShowFilterHelper(AudioPin, FilterGraph)) == S_OK )
                AddLrcFilterFlag = true;
            if( AudioPin ) AudioPin->Release();
     }
     // Release enumerater
     EnumFilters->Release();
 
     if( AddLrcFilterFlag ) return S_OK;
     else return E_FAIL;
}

CComPtr<IPin> CLrcShowFilterWrapper::GetOnePin(IBaseFilter * Filter, PIN_DIRECTION Direction)
{
     ASSERT(Filter != NULL);

     CComPtr<IPin> Pin;
     IEnumPins * EnumPins = NULL;
     PIN_DIRECTION   PinDirection;

     HRESULT HR = Filter->EnumPins(&EnumPins);
     if( HR != S_OK ) return NULL;

     while( S_OK == (HR = EnumPins->Next(1, &Pin, NULL)) )
     {
            if( Pin->QueryDirection(&PinDirection) == S_OK && 
                PinDirection == Direction )
                break;
            Pin.Release();
     }
     EnumPins->Release();

     return Pin;
}

HRESULT CLrcShowFilterWrapper::RemoveLrcShowFilter(IGraphBuilder* FilterGraph)
{
     return FilterGraph->RemoveFilter(m_LrcShowFilter);
}

HRESULT CLrcShowFilterWrapper::CreateLrcFilter()
{
     HRESULT HR = CoCreateInstance(CLSID_LrcShowFilter, 
                                   NULL,
                                   CLSCTX_INPROC_SERVER,
                                   IID_IBaseFilter,
                                   (void **) &m_LrcShowFilter);
     if( FAILED(HR) )
     {
         m_LrcShowFilter = NULL;
         return HR;
     }

     return S_OK;
}


void CLrcShowFilterWrapper::InitLrcFilter(const MusicBoxSetting & Setting)
{
     ILrcShowFilter * FilterInterface = NULL;
     HRESULT HR = m_LrcShowFilter->QueryInterface(IID_ILrcShowFilter, (void **)&FilterInterface);
     if( HR == S_OK )
     {
         // Set lrc show mode
         FilterInterface->SetShowMode(Setting.GetShowMode()==DeskMode);
         // Window lrc show initialization
         FilterInterface->SetWinAutoResizeMode(Setting.m_WinProp.m_AutoResizeMode);
         FilterInterface->SetWinFont(&Setting.m_WinProp.m_LrcFont);
         FilterInterface->SetWinUnFocusClr(Setting.m_WinProp.m_CommClr);
         FilterInterface->SetWinFocusClr(Setting.m_WinProp.m_FocusClr);
         FilterInterface->SetWinBgClr(Setting.m_WinProp.m_BgClr);
         FilterInterface->SetWinKaraokeMode(Setting.m_WinProp.m_KaraokeMode);
         FilterInterface->SetWinGradientMode(Setting.m_WinProp.m_GradientMode);
         FilterInterface->SetWinTopMostMode(Setting.m_WinProp.m_TopMostMode);
         FilterInterface->SetWinAlignmentOpt(Setting.m_WinProp.m_AlignmentOpt);

         // Desktop lrc show initialization
         FilterInterface->SetDeskKaraokeMode(Setting.m_DeskProp.m_KaraokeMode);
         FilterInterface->SetDeskShadowMode(Setting.m_DeskProp.m_ShadowMode);
         FilterInterface->SetDeskOutlineMode(Setting.m_DeskProp.m_OutlineMode);
         FilterInterface->SetDeskTopMostMode(Setting.m_DeskProp.m_TopMostMode);
         FilterInterface->SetDeskTransMode(Setting.m_DeskProp.m_WinTransMode);
         FilterInterface->SetDeskBgTransparent(Setting.m_DeskProp.m_BgTransparent);
         FilterInterface->SetDeskLrcTransparent(Setting.m_DeskProp.m_LrcTransparent);
         FilterInterface->SetDeskBgClr(Setting.m_DeskProp.m_BgClr);
         FilterInterface->SetDeskFocusClr(Setting.m_DeskProp.m_FocusClr[0],
                                          Setting.m_DeskProp.m_FocusClr[1%Setting.m_DeskProp.m_FocusClrNum],
                                          Setting.m_DeskProp.m_FocusClr[2%Setting.m_DeskProp.m_FocusClrNum]);
         FilterInterface->SetDeskUnFocusClr(Setting.m_DeskProp.m_UnFocusClr[0],
                                            Setting.m_DeskProp.m_UnFocusClr[1%Setting.m_DeskProp.m_UnFocusClrNum],
                                            Setting.m_DeskProp.m_UnFocusClr[2%Setting.m_DeskProp.m_UnFocusClrNum]);
         FilterInterface->SetDeskFont(&Setting.m_DeskProp.m_LrcFont);

         // Release interface
         FilterInterface->Release();
     }
}

bool CLrcShowFilterWrapper::StartLrcFilter(const MusicBoxSetting & Setting)
{
     if( m_LrcShowFilter && !Setting.GetCurLrcFullPath().empty() )
     {
         ILrcShowFilter * FilterInterface = NULL;
         HRESULT HR = m_LrcShowFilter->QueryInterface(IID_ILrcShowFilter, (void **)&FilterInterface);
         if( HR == S_OK )
         {
             // Set song's duration time
             FilterInterface->SetDuration(Setting.GetSongDura());
             if( Setting.GetShowMode() == WinMode )
             {
                 // Window lrc show
                 FilterInterface->SetWinShowWnd(Setting.m_WinLrcShowDlg->m_hWnd);
                 FilterInterface->SetWinLrcFile(Setting.GetCurLrcFullPath().c_str());
                 FilterInterface->StartWinShow();
             }else{
                 // Desktop lrc show
                 FilterInterface->SetDeskShowWnd(Setting.m_DeskLrcShowDlg->m_hWnd);
                 FilterInterface->SetDeskLrcFile(Setting.GetCurLrcFullPath().c_str());
		         FilterInterface->StartDesktopShow();
             }
             FilterInterface->Release();
       
             return true;
        }
     }

     return false;
}

bool CLrcShowFilterWrapper::StopLrcFilter(const MusicBoxSetting & Setting)
{
     if( m_LrcShowFilter )
     {
         ILrcShowFilter * FilterInterface = NULL;
         HRESULT HR = m_LrcShowFilter->QueryInterface(IID_ILrcShowFilter, (void **)&FilterInterface);
         if( HR == S_OK )
         {
             if( Setting.GetShowMode() == WinMode )
                 FilterInterface->StopWinShow();
             else
		         FilterInterface->StopDesktopShow();
             
             FilterInterface->Release();
             return true;
        }
     }

     return false;
}

IBaseFilter * CLrcShowFilterWrapper::operator->() const _THROW0()
{	
     // Return pointer to class object
     return  m_LrcShowFilter;
}

HRESULT CLrcShowFilterWrapper::Release()
{
     IBaseFilter * Tmp = m_LrcShowFilter;
     m_LrcShowFilter = NULL;
     return Tmp ? Tmp->Release(): S_OK;
}

void CLrcShowFilterWrapper::ReloadDeskLrc(const tstring & LrcFullPath)
{
     ILrcShowFilter * FilterInterface = NULL;
     HRESULT HR = m_LrcShowFilter->QueryInterface(IID_ILrcShowFilter, (void **)&FilterInterface);
     if( HR == S_OK )
     {
         FilterInterface->ReloadDeskLrcFile(LrcFullPath.c_str());
         FilterInterface->Release();
     }
}
     
void CLrcShowFilterWrapper::ReloadWinLrc(const tstring & LrcFullPath)
{
     ILrcShowFilter * FilterInterface = NULL;
     HRESULT HR = m_LrcShowFilter->QueryInterface(IID_ILrcShowFilter, (void **)&FilterInterface);
     if( HR == S_OK )
     {
         FilterInterface->ReloadWinLrcFile(LrcFullPath.c_str());
         FilterInterface->Release();
     }
}